#include <math.h>
#include <stdio.h>
#include <time.h>

#include "calculs.h"

/**
 * Calcul du jour dans l'année selon la formule de
 * N. Derhowitz et E. M. Reingold, Calendrical
 * calculations, third edition, eq. 2.1 p 46
 */
int
date_to_JDA_DnR (int annee, int mois, int jour)
{
  int corr = 0;
  /* Arguments hors gamme. */
  if (annee < 0 || mois <= 0 || mois > 12 || jour <= 0 || jour > 31)
    return -1;

  /* */
  if (mois > 2)
    {
      if (gregor_bissextile (annee))
	{
	  corr = -1;
	}
      else
	{
	  corr = -2;
	}
    }

  return floor ((367 * mois - 362) / 12.) + corr + jour;
}

/** Vérifie que Date_to_JDA renvoie la valeur attendue
 *
 * \return 0 si OK,
 *         1 si NOK
 */
int
check_DtQ_result (int y, int m, int d, int e_Q)
{
  if (date_to_JDA (y, m, d) != e_Q)
    {
      fprintf (stderr, "date_to_JDA (%d, %d, %d) = %d\n", y, m, d,
	       date_to_JDA (y, m, d));
      return 1;
    }
  return 0;
}

/** Vérifie que JDA_to_Date renvoie les valeurs attendues
 *
 * \return 0 si OK,
 *         1 si NOK
 */
int
check_QtD_result (int Q, unsigned int y, unsigned int e_y, unsigned int e_m,
		  unsigned int e_d)
{
  unsigned int m, d;

  JDA_to_date (Q, &y, &m, &d);
  if ((m != e_m) || (d != e_d))
    {
      fprintf (stderr,
	       "Apres JDA_to_date (%d, %d, m, d), m = %d, d=%d\n", Q, y, m,
	       d);
      return 1;
    }
  return 0;
}

const char *desc_DtQ_J1 = "Date_to_JDA (y, 1, 1) == 1";

int
check_DtQ_J1 ()
{
  int year;
  int result = 0;
  for (year = 1583; year < 3000; year++)
    {
      result += check_DtQ_result (year, 1, 1, 1);
    }
  return result;
}

const char *date_to_JDA_naif_eq_DnR =
  "Date_to_JDA (y, m, d) == Date_to_JDA_DnR (y, m, d)";

int
check_date_to_JDA_naif_eq_DnR ()
{
  int year, month, day;
  int J_DnR, J_naif;
  int result = 0;

  for (year = 1583; year < 3000; year++)
    {
      for (month = 1; month < 13; month++)
	{
	  for (day = 1; day < 29; day++)
	    {
	      J_DnR = date_to_JDA_DnR (year, month, day);
	      J_naif = date_to_JDA (year, month, day);
	      if (J_DnR != J_naif)
		{
		  fprintf (stderr, "%04d-%02d-%02d : D&R=%d, naif=%d\n",
			   year, month, day, J_DnR, J_naif);
		  result++;
		}
	    }
	}
    }
  return result;
}

const char *date_to_JDA_naif_eq_time =
  "Date_to_JDA (y, m, d) == mktime(tm(y, m, d)).yday";

int
check_date_to_JDA_naif_eq_time ()
{
  int year, month, day;
  int J_naif;
  int result = 0;
  struct tm tm_s;
  time_t x;

  tm_s.tm_sec = 0;
  tm_s.tm_min = 0;
  for (year = 1902; year < 2038; year++)
    {
      for (month = 1; month < 13; month++)
	{
	  for (day = 1; day < 29; day++)
	    {
	      tm_s.tm_year = year - 1900;
	      tm_s.tm_mon = month - 1;
	      tm_s.tm_mday = day;
	      tm_s.tm_hour = 12;
	      tm_s.tm_isdst = 0;
	      x = mktime (&tm_s);
	      J_naif = date_to_JDA (year, month, day);
	      if (J_naif != tm_s.tm_yday + 1)
		{
		  fprintf (stderr,
			   "%04d-%02d-%02d : tm_struct.tm_yday=%d, naif=%d\n",
			   year, month, day, tm_s.tm_yday + 1, J_naif);
		  result++;
		}
	    }
	}
    }
  return result;
}

const char *desc_DtQ_29fev =
  "Date_to_JDA (y, 2, 29) == 60 en année bissextile";

int
check_DtQ_29fev ()
{
  int year, result = 0;

  for (year = 1584; year < 3000; year = year + 4)
    {
      if ((year % 100 != 0) || (year % 400 == 0))
	{
	  result += check_DtQ_result (year, 2, 29, 60);
	}
    }
  return result;
}

const char *desc_DtQ_1mars_bissext =
  "Date_to_JDA (y, 3, 1) == 61 en année bissextile";

int
check_DtQ_1mars_bissext ()
{
  int year, result = 0;

  for (year = 1584; year < 3000; year = year + 4)
    {
      if ((year % 100 != 0) || (year % 400 == 0))
	{
	  result += check_DtQ_result (year, 3, 1, 61);
	}
    }
  return result;
}

const char *desc_DtQ_31dec_bissext =
  "Date_to_JDA (y, 12, 31) == 366 en année bissextile";

int
check_DtQ_31dec_bissext ()
{
  int year, result = 0;

  for (year = 1584; year < 3000; year = year + 4)
    {
      if ((year % 100 != 0) || (year % 400 == 0))
	{
	  result += check_DtQ_result (year, 12, 31, 366);
	}
    }
  return result;
}

const char *desc_DtQ_31dec_mul100 =
  "Date_to_JDA (y, 12, 31) == 365 en année *100";

int
check_DtQ_31dec_mul100 ()
{
  int year, result = 0;

  for (year = 1600; year < 3000; year = year + 100)
    {
      if (year % 400 != 0)
	{
	  result += check_DtQ_result (year, 12, 31, 365);
	}
    }
  return result;
}


const char *desc_QtD_J1 = "JDA_to_Date (1, y, m, d) donne m=1 && d=1";

int
check_QtD_J1 ()
{
  unsigned int y;
  int result = 0;
  for (y = 1582; y < 3000; y++)
    {
      result += check_QtD_result (1, y, y, 1, 1);
    }
  return result;
}

const char *desc_QtD_29fev =
  "JDA_to_Date (60, y, m, d) donne m=2 && d=29 en années bissext.";

int
check_QtD_29fev ()
{
  unsigned int y;
  int result = 0;
  for (y = 1584; y < 3000; y = y + 4)
    {
      if ((y % 100 != 0) || (y % 400 == 0))
	{
	  result += check_QtD_result (60, y, y, 2, 29);
	}
    }
  return result;
}

const char *desc_QtD_1mars_bissext =
  "JDA_to_Date (61, y, m, d) donne m=3 && d=1 en années bissext.";

int
check_QtD_1mars_bissext ()
{
  unsigned int y;
  int result = 0;
  for (y = 1584; y <= 3000; y = y + 4)
    {
      if ((y % 100 != 0) || (y % 400 == 0))
	{
	  result += check_QtD_result (61, y, y, 3, 1);
	}
    }
  return result;
}

const char *desc_QtD_31dec_bissext =
  "JDA_to_Date (366, y, m, d) donne m=12 && d=31 en années bissext.";

int
check_QtD_31dec_bissext ()
{
  unsigned int y;
  int result = 0;
  for (y = 1584; y <= 3000; y = y + 4)
    {
      if ((y % 100 != 0) || (y % 400 == 0))
	{
	  result += check_QtD_result (366, y, y, 12, 31);
	}
    }
  return result;
}

const char *desc_QtD_31dec_mul100 =
  "JDA_to_Date (365, y, m, d) donne m=12 && d=31 en années *100";

int
check_QtD_31dec_mul100 ()
{
  int y, result = 0;

  for (y = 1600; y <= 3000; y = y + 100)
    {
      if (y % 400 != 0)
	{
	  result += check_QtD_result (365, y, y, 12, 31);
	}
    }
  return result;
}

int
main (int argc, char *argv[])
{
#define NBR_TEST 12

  const char *desc[NBR_TEST];
  int i, result, nb_fail;
  int (*tab_fct[NBR_TEST]) ();

  i = 0;
  tab_fct[i] = check_DtQ_J1;
  desc[i] = desc_DtQ_J1;
  i++;
  tab_fct[i] = check_date_to_JDA_naif_eq_DnR;
  desc[i] = date_to_JDA_naif_eq_DnR;
  i++;
  tab_fct[i] = check_date_to_JDA_naif_eq_time;
  desc[i] = date_to_JDA_naif_eq_time;
  i++;
  tab_fct[i] = check_DtQ_29fev;
  desc[i] = desc_DtQ_29fev;
  i++;
  tab_fct[i] = check_DtQ_1mars_bissext;
  desc[i] = desc_DtQ_1mars_bissext;
  i++;
  tab_fct[i] = check_DtQ_31dec_bissext;
  desc[i] = desc_DtQ_31dec_bissext;
  i++;
  tab_fct[i] = check_DtQ_31dec_mul100;
  desc[i] = desc_DtQ_31dec_mul100;
  i++;
  tab_fct[i] = check_QtD_J1;
  desc[i] = desc_QtD_J1;
  i++;
  tab_fct[i] = check_QtD_29fev;
  desc[i] = desc_QtD_29fev;
  i++;
  tab_fct[i] = check_QtD_1mars_bissext;
  desc[i] = desc_QtD_1mars_bissext;
  i++;
  tab_fct[i] = check_QtD_31dec_bissext;
  desc[i] = desc_QtD_31dec_bissext;
  i++;
  tab_fct[i] = check_QtD_31dec_mul100;
  desc[i] = desc_QtD_31dec_mul100;
  i++;

  nb_fail = 0;
  for (i = 0; i < NBR_TEST; i++)
    {
      result = tab_fct[i] ();
      fprintf (stdout, " [%d] %s :", i + 1, desc[i]);
      if (result == 0)
	{
	  fprintf (stdout, " ... passed\n");
	}
      else
	{
	  fprintf (stdout, " ... failed (%d)\n", result);
	  nb_fail++;
	}
    }
  fprintf (stdout, "Tests run: %d, Failures: %d\n", i, nb_fail);

  return (nb_fail == 0);
}
